package FIT::TypeAdapter::Base;

use FIT::Class;
use FIT::Util::SpecialChars;
use FIT::Document::Transformer;

has name      => (is => 'rw', isa => 'Str'   , required => 1);
has adaptee   => (is => 'rw', isa => 'Object', required => 1); # TODO:weak?
has type_from => (is => 'rw', isa => 'Object'); # where to get type info from

has type_constraint =>
    (is => 'ro', isa => 'Moose::Meta::TypeConstraint', required => 1);

# set the value from the cell into the adaptee
sub parse_and_set {
    my $cell  = shift;
    die "Can't parse a cell marked blank" if $cell->detect_mark($MARKER_BLANK);
    my $value = $self->parse($cell);
    $self->set($value);
}

# check value from cell vs. value from adaptee
# or just paint actual if cell is marked blank
sub parse_and_check {
    my $cell = shift;
    return $self->handle_blank($cell) if $cell->detect_mark($MARKER_BLANK);

    my $actual   = $self->get;
    my $expected = $self->parse($cell);
    my $result   = $self->compare($actual, $expected, $cell);
    $self->paint($cell, $actual, $expected, $result);
}

sub handle_blank {
    my $cell = shift;
    my $actual = $self->get;
    $self->paint_actual($cell, $actual);
}

# get the value from the adaptee
# if compute_$attribute() exists, we call it
# else we try $attribute() directly
sub get {
    my $adaptee = $self->adaptee;
    my $name    = $self->name;
    my $compute = "compute_$name";
    return $adaptee->can($compute)? $adaptee->$compute: $adaptee->$name;
}

# set the value on the adaptee
sub set {
    my $value = shift;
    my $name  = $self->name;
    $self->adaptee->$name( $value );
}

# get the value from the cell
sub parse {
    my $cell = shift;
    my $text = $self->parse_from_string( $cell->fit_text );
    $self->validate($text);
    return $text;
}

sub validate {
    my $value = shift;
    my $message = $self->type_constraint->validate($value);
    die "Can't adapt [". $self->name. "] of type [". (ref $self). "], because- $message"
        if defined $message;
}

# TODO: make paint use paint actual so you dont need to implement both
sub paint {
    my ($cell, $actual, $expected, $result) = @_; # result is true if all went well
    transform {
        $result? Right:
                 Wrong actual was $self->as_string($actual, $expected, $result, $cell);
    } $cell;
}

# paint the actual into a cell
sub paint_actual {
    my ($cell, $actual) = @_;
    $actual = '' unless defined $actual; # TODO special undef symbol
    transform { Note text is $self->as_string($actual) } $cell;
}

sub as_string { shift }

# these need to work as object or class methods- don't access adapter state ----

sub parse_from_string { shift }

# compare value from cell vs. value from adaptee
# TODO: return message as well
sub compare {
    my ($actual, $expected) = @_;
    return 1 if !defined($actual) && !defined($expected);
    return 0 if !defined($actual) || !defined($expected);
    return $expected eq $actual;
}

